TimeGrid tg; 60=>int tempo; 60=>int middleC; //32nd notes (divided by 8), 32 per measure, 8 meas / section tg.set(1::minute/tempo/8, 32, 8); tg.sync(); //sync to beat //-------------------------------------------------------------------------------------------------- //GLOBAL VARIABLES [1,2,4,8,16,32] @=> int notelength[]; [0,8,16,24] @=> int quarternotes[]; [0,4,8,12,16,20,24,28] @=> int eighthnotes[]; [0,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30] @=> int sixteenthnotes[]; [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31] @=> int thirty2notes[]; //--------------------------------------------------------------------------------------------------- //INSTRUMENT DECLARATIONS //flute chords Flute module1s => dac; Flute module1a => dac; Flute module1t => dac; //sax chords Saxofony module2s => dac; Saxofony module2a => dac; Saxofony module2t => dac; //blowbotl chords BlowBotl module3s => dac; BlowBotl module3a => dac; BlowBotl module3t => dac; //arpeggiated guitar StifKarp module4 => dac; //scaling clarinet Clarinet module5 => dac; //scaling bowed Bowed module6 => dac; //bass StifKarp module7 => dac; //-------------------------------------------------------------------------------------------------- //SCALES //major and minor [0, 2, 4, 5, 7, 9, 11] @=> int major[]; [0, 2, 3, 5, 7, 8, 11] @=> int harminor[]; [0, 2, 3, 5, 7, 8, 11] @=> int natminor[]; [0, 2, 3, 5, 7, 8, 11] @=> int melminor[]; //pentatonics [0, 2, 4, 7, 9] @=> int majorpent[]; [0, 3, 5, 7, 10] @=> int minorpent[]; [0, 2, 3, 7, 8] @=> int hirajoshi[]; [major,harminor,natminor,melminor,majorpent,minorpent,hirajoshi] @=> int scalelist[][]; //scale selecting function. a is the degree of the scale, and sc the actual scale. fun int scale(int a, int sc[]) { sc.cap() => int n; //number of degrees in scale a/n => int o; //octave being requested, number of wraps a%n => a; //wrap the note within first octave if ( a<0 ) { //cover the negative border case a + 12 => a; o - 1=> o; } //each octave contributes 12 semitones, plus the scale return o*12 + sc[a]; } //--------------------------------------------------------------------------------------------------- //BASSLINES/CHORD PROGRESSIONS //I, IV, V, ii, vi, V, IV, V [0, 3, 4, 1, 5, 4, 3, 4] @=> int bass[]; [48,51,52,49,53,52,51,52] @=> int bassmidi[]; //I,V,vi,ii,IV,iii,ii,V [0, 4, 5, 1, 3, 2, 1, 4] @=> int bass2[]; [48,52,53,49,51,50,49,52] @=> int bassmidi2[]; [0, 1, 0, 4, 5, 4, 0, 0 ] @=> int bass3[]; [48,50,48,55,57,55,48,48] @=> int bassmidi3[]; fun int[] basstomidi(int noteinput[]){ noteinput @=> int buffer[]; 48 @=> int cbelowc; for(0=> int po; po < noteinput.cap(); po++){ <<>>; } } //--------------------------------------------------------------------------------------------------- fun int[] scaletomidi(int scale[], int key) { scale @=> int newarray[]; for(0=> int i; i< scale.cap(); i++){ scale[i]+key=>newarray[i]; <<>>; } return newarray; } //--------------------------------------------------------------------------------------------------- // //--------------------------------------------------------------------------------------------------- //ARPEGGIOS AND SCALE ARRAYS [0,0,0,0,0,0,0,0] @=> int arpeg0[]; [0,2,4,2,0,2,4,2] @=> int arpeg1[]; [0,2,4,6,0,2,4,6] @=> int arpeg2[]; [0,2,4,2,6,4,7,6] @=> int arpeg3[]; [0,2,4,6,8,6,4,2] @=> int arpeg4[]; [0,2,4,0,2,4,0,2] @=> int arpeg5[]; [2,4,0,2,4,0,2,4] @=> int arpeg6[]; [0,2,4,5,0,2,4,5] @=> int arpeg7[]; [0,1,2,0,1,2,0,1] @=> int arpeg8[]; [0,4,0,4,4,0,4,0] @=> int arpeg9[]; [0,4,0,4,0,4,4,0] @=> int arpeg10[]; [0,4,0,4,0,0,4,0] @=> int arpeg11[]; [0,4,0,4,0,4,0,0] @=> int arpeg12[]; [0,4,0,4,0,0,0,0] @=> int arpeg13[]; [4,0,0,4,0,0,4,0] @=> int arpeg14[]; [4,0,0,4,4,0,0,4] @=> int arpeg15[]; [0,4,4,3,4,2,3,4] @=> int arpeg16[]; [7,0,0,7,0,0,7,0] @=> int arpeg17[]; [0,7,0,0,7,0,0,7] @=> int arpeg18[]; [7,0,7,0,0,7,0,7] @=> int arpeg19[]; [7,0,4,0,4,0,7,0] @=> int arpeg20[]; [arpeg0,arpeg1,arpeg2,arpeg3,arpeg4,arpeg5,arpeg6,arpeg7,arpeg8,arpeg9,arpeg10,arpeg11,arpeg12,arpeg13,arpeg14,arpeg15, arpeg16, arpeg17, arpeg18, arpeg19, arpeg20] @=> int arpeglist[][]; fun int arp(int a, int oct, int deg[]) { deg.cap() => int n; //number of arp degrees a/n => int o; //number of octaves up/down a%n => a; //after subtracting the octaves if ( a<0 ) { //the border case a + n => a; o - 1 => o; } return o*oct + deg[a]; } //-------------------------------------- [0,1,2,0,1,0,1,2] @=> int scaler0[]; [0,1,2,0,1,2,0,1] @=> int scaler1[]; [0,1,2,3,0,1,2,3] @=> int scaler2[]; [0,1,2,1,2,3,2,3] @=> int scaler3[]; [0,1,2,0,1,4,3,2] @=> int scaler4[]; [0,1,2,3,4,2,3,4] @=> int scaler5[]; [0,1,2,0,1,4,3,4] @=> int scaler6[]; [0,4,3,4,2,4,1,4] @=> int scaler7[]; [0,7,6,7,5,7,4,7] @=> int scaler8[]; [7,6,5,6,7,4,5,6] @=> int scaler9[]; [0,1,0,1,2,1,2,1] @=> int scaler10[]; [0,1,0,1,2,1,2,1] @=> int scaler11[]; [0,0,1,0,0,1,0,1] @=> int scaler12[]; [0,0,1,2,0,0,1,2] @=> int scaler13[]; [0,0,1,0,0,0,2,0] @=> int scaler14[]; [0,2,1,2,3,4,2,1] @=> int scaler15[]; [scaler0,scaler1,scaler2,scaler3,scaler4,scaler5,scaler6,scaler7,scaler8,scaler9,scaler10,scaler11,scaler12,scaler13,scaler14,scaler15] @=> int scalerlist[][]; //--------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------- [48,50,48,55,57,55,48,48] @=> int bassLine[]; [0,0,0,0,0,0,0,0] @=> int inversions[]; [0,0,0,0,0,0,0,0] @=> int noteValues[]; 1 => int majorc; //1 for majorc, 0 for minor 60 => int key; //C4 = 60 550::ms => dur update; //Other globals (generated) bassLine.cap() => int numNotes; int sopranoLine[numNotes]; int altoLine[numNotes]; int tenorLine[numNotes]; //Print note letter and octave (e.g., C4) fun void PrintNote(int midiNum) { if(midiNum == 0) { <<<"none">>>; return; } midiNum % 12 => int note; (midiNum / 12) - 1 => int octave; if(note == 0) <<<"C", octave>>>; if(note == 1) <<<"C#", octave>>>; if(note == 2) <<<"D", octave>>>; if(note == 3) <<<"D#", octave>>>; if(note == 4) <<<"E", octave>>>; if(note == 5) <<<"F", octave>>>; if(note == 6) <<<"F#", octave>>>; if(note == 7) <<<"G", octave>>>; if(note == 8) <<<"G#", octave>>>; if(note == 9) <<<"A", octave>>>; if(note == 10) <<<"A#", octave>>>; if(note == 11) <<<"B", octave>>>; } //returns 0 if chord is majorc, 1 if minor, 2 if diminished fun int GetQuality(int bass, int inversion) { key % 12 => int tonic; int root; Std.abs((bass % 12) - tonic) => int bassDegree; if(inversion == 0) bass => root; else if(inversion == 1 && majorc == 1) { if(bassDegree == 0 || bassDegree == 5 || bassDegree == 7) //minor return 1; if(bassDegree == 4 || bassDegree == 9 || bassDegree == 11) //majorc return 0; if(bassDegree == 2) //diminished return 2; } else if(inversion == 1 && majorc == 0) { if(bassDegree == 3 || bassDegree == 8 || bassDegree == 10) return 1; if(bassDegree == 0 || bassDegree == 7 || bassDegree == 11) return 0; if(bassDegree == 2 || bassDegree == 5) return 2; } else if(inversion == 2 && majorc == 1) { if(bassDegree == 4 || bassDegree == 9 || bassDegree == 11) //minor return 1; if(bassDegree == 0 || bassDegree == 2 || bassDegree == 7) //majorc return 0; if(bassDegree == 5) return 2; } else if(inversion == 2 && majorc == 0) { if(bassDegree == 0 || bassDegree == 7) //minor return 1; if(bassDegree == 2 || bassDegree == 3 || bassDegree == 10) //majorc return 0; if(bassDegree == 5 || bassDegree == 8) return 2; } Std.abs((root % 12) - tonic) => int interval; if(majorc == 1) { //key of composition is majorc if(interval == 2 || interval == 4 || interval == 9) //minor return 1; if(interval == 11) //diminished return 2; } else if(majorc == 0) { //key of composition is minor if(interval == 2 || interval == 11) //dimished return 2; if(interval == 0 || interval == 5) //minor return 1; } return 0; //else majorc } //Removes every octave of the given note from the array fun void RemoveNote(int notes[], int note) { for(0 => int i; i < notes.cap(); i++) if((note - notes[i])%12 == 0) 0 => notes[i]; } //Returns an array of all the possible notes that could accompany //the given bass fun int[] GetChordNotes(int bass, int inversion) { int notes[12]; int root; GetQuality(bass, inversion) => int quality; //0 = majorc, 1 = minor, 2 = diminished //****added minor if(inversion == 0) bass => root; else if(inversion == 1 && quality == 0) bass - 4 => root; //majorc 3rd else if(inversion == 1 && (quality == 1 || quality == 2)) bass - 3 => root; //minor 3rd else if(inversion == 2 && quality != 2) bass - 7 => root; //5th else if(inversion == 2 && quality == 2) bass - 6 => root; //diminished 5th //do four octaves root => int curNote; for(0 => int i; i < 4; i++) { i*3 => int index; curNote => notes[index]; if(quality == 0) { curNote + 4 => notes[index+1]; //majorc third curNote + 7 => notes[index+2]; //fifth } else if(quality == 1) { curNote + 3 => notes[index+1]; //minor third curNote + 7 => notes[index+2]; } else if(quality == 2) { curNote + 3 => notes[index+1]; curNote + 6 => notes[index+2]; //diminished fifth (!) } 12 +=> curNote; //go up one octave } return notes; } //Returns index of element if found, -1 otherwise fun int Contains(int a[], int elem) { for(0 => int i; i < a.cap(); i++) if(a[i] == elem) return i; return -1; } //Returns a copy of the given array fun int[] Copy(int a[]) { int cp[a.cap()]; for(0 => int i; i < a.cap(); i++) a[i] => cp[i]; return cp; } //Keep common tones the same between chords //Can only be called on chords index 1 or later (requires previous chord) fun int FillCommonTones(int notes[], int index) { 0 => int count; //tenor: tenorLine[index - 1] => int prevNote; Contains(notes, prevNote) => int common; if(common != -1) { //common tone! notes[common] => tenorLine[index]; 1 +=> count; } //alto: altoLine[index - 1] => prevNote; Contains(notes, prevNote) => common; if(common != -1) { //common tone! notes[common] => altoLine[index]; 1 +=> count; } //soprano: sopranoLine[index - 1] => prevNote; Contains(notes, prevNote) => common; if(common != -1) { //common tone! notes[common] => sopranoLine[index]; 1 +=> count; } return count; } //returns 0 if no parallel octaves/fifths are found, 1 otherwise fun int Parallels(int soprano, int alto, int tenor, int index) { bassLine[index] => int bass; [soprano, alto, tenor, bass] @=> int newVoices[]; [sopranoLine[index - 1], altoLine[index - 1], tenorLine[index - 1], bassLine[index - 1]] @=> int prevVoices[]; for(3 => int j; j >= 0; j--) { for(3 => int k; k >= 0; k--) { if(j == k) continue; //same voice //if((prevVoices[k] - prevVoices[j]) % 12 == 0) //this is an octave // if((newVoices[k] - newVoices[j]) % 12 == 0) //another octave // if(newVoices[k] != prevVoices[k] && newVoices[j] != prevVoices[j]) // return 1; //^since if a voices doesn't change, there's no motion if((prevVoices[k] - prevVoices[j]) % 7 == 0) //this is a fifth if((newVoices[k] - newVoices[j]) % 7 == 0) //another fifth if(newVoices[k] != prevVoices[k] && newVoices[j] != prevVoices[j]) return 1; } } return 0; } //Given choices of chord voicings, pick the "best" one //(whichever requires the least amount of motion) fun void PickBest(int sopranos[], int tenors[], int altos[], int index) { 999 => int best; 0 => int bestPos; for(0 => int i; i< sopranos.cap(); i++) { if(sopranos[i] == 0) break; 0 => int heuristic; Std.abs(sopranos[i] - sopranoLine[index - 1]) +=> heuristic; Std.abs(tenors[i] - tenorLine[index - 1]) +=> heuristic; Std.abs(altos[i] - altoLine[index - 1]) +=> heuristic; if(heuristic < best) { heuristic => best; i => bestPos; } } if(best == 999) <<<"PROBLEM...hopefully this is doesn't happen">>>; sopranos[bestPos] => sopranoLine[index]; altos[bestPos] => altoLine[index]; tenors[bestPos] => tenorLine[index]; } //Returns 1 if 3 distinct notes are present, 0 otherwise fun int CheckAll(int soprano, int alto, int tenor, int index) { bassLine[index] => int bass; [soprano, alto, tenor, bass] @=> int chord[]; 0 => int repeats; for(0 => int j; j < chord.cap(); j++) { for(j => int k; k < chord.cap(); k++) { if(j == k) continue; if(chord[j] % 12 == chord[k] % 12) 1 +=> repeats; if(repeats > 1) return 0; } } return 1; } //Calculate possible voicings for chord fun void FillNotes(int notes[], int index) { 0 => int doubled; //boolean, 0 indicates root hasn't been doubled //will only be updated if in root position int root; bassLine[index] => int bass; inversions[index] => int inversion; //*****add minor functionality: GetQuality(bass, inversion) => int quality; if(inversion == 0) bass => root; else if(inversion == 1 && quality == 0) bass - 4 => root; //majorc 3rd else if(inversion == 1 && (quality == 1 || quality == 2)) bass - 3 => root; //minor 3rd else if(inversion == 2 && quality != 2) bass - 7 => root; //5th else if(inversion == 2 && quality == 2) bass - 6 => root; //diminished 5th //Remove common tones from notes: if(sopranoLine[index] != 0) { RemoveNote(notes, sopranoLine[index]); if(sopranoLine[index] % 12 == root % 12 && inversion == 0) 1 => doubled; } if(altoLine[index] != 0) { RemoveNote(notes, altoLine[index]); if(altoLine[index] % 12 == root % 12 && inversion == 0) 1 => doubled; } if(tenorLine[index] != 0) { RemoveNote(notes, tenorLine[index]); if(tenorLine[index] % 12 == root % 12 && inversion == 0) 1 => doubled; } 0 => int commonToneDoubles; if(doubled == 1) 1 => commonToneDoubles; //store all potential arrangements of chord (up to 5) int sopranos[5]; int altos[5]; int tenors[5]; 0 => int pos; for(0 => int x; x < notes.cap(); x++) { int tenorNote; Copy(notes) @=> int noTenor[]; if(tenorLine[index] == 0) { notes[x] => tenorNote; if(tenorNote == 0) continue; if(tenorNote <= bass) continue; if(tenorNote < 48) continue; //check range if(tenorNote > 67) continue; if(tenorNote % 12 == root % 12 && doubled == 0 && inversion == 0) 1 => doubled; else if(inversion == 0) RemoveNote(noTenor, tenorNote); } else { //already have common tone tenorLine[index] => tenorNote; notes.cap() => x; //kill for-loop } for(0 => int y; y < notes.cap(); y++) { //if a note has already been filled in, it is a common tone, //and this loop is unnecessary int altoNote; Copy(noTenor) @=> int noAlto[]; if(altoLine[index] == 0) { noTenor[y] => altoNote; if(altoNote == 0) continue; if(altoNote <= tenorNote) continue; if(altoNote < 65) continue; if(altoNote > 72) continue; if(altoNote % 12 == root % 12 && doubled == 0 && inversion == 0) 1 => doubled; else if(altoNote % 12 == root % 12 && doubled == 1) continue; else if(inversion == 0) RemoveNote(noAlto, altoNote); } else { //a common tone has already been filled in altoLine[index] => altoNote; notes.cap() => y; //kill for-loop } for(0 => int z; z < notes.cap(); z++) { int sopranoNote; if(sopranoLine[index] == 0) { noAlto[z] => sopranoNote; if(sopranoNote == 0) continue; if(sopranoNote <= altoNote) continue; if(sopranoNote < 60) continue; if(sopranoNote > 79) continue; if(sopranoNote % 12 == root % 12 && doubled == 0 && inversion == 0) 1 => doubled; else if(sopranoNote % 12 == root % 12 && doubled == 1) continue; } else { //already have common tone sopranoLine[index] => sopranoNote; notes.cap() => z; //kill for-loop } if(doubled == 0 && inversion == 0) continue; // check doubling if(inversion != 0 && CheckAll(sopranoNote, altoNote, tenorNote, index) == 0) continue; if(Parallels(sopranoNote, altoNote, tenorNote, index) > 0) continue; if(pos >= sopranos.cap()) continue; //if all is well: sopranoNote => sopranos[pos]; altoNote => altos[pos]; tenorNote => tenors[pos]; 1 +=> pos; if(sopranoNote % 12 == root % 12 && commonToneDoubles == 0) 0 => doubled; } if(altoNote % 12 == root % 12 && commonToneDoubles == 0) 0 => doubled; } if(tenorNote % 12 == root % 12 && commonToneDoubles == 0) 0 => doubled; } //now choose the best chord arrangement to use: PickBest(sopranos, tenors, altos, index); } //Call necessary functions to generate appropriate chord voicing fun void FillChord(int index) { bassLine[index] => int bassNote; inversions[index] => int inversion; GetChordNotes(bassNote, inversion) @=> int allNotes[]; FillCommonTones(allNotes, index) => int num; if(num < 3) FillNotes(allNotes, index); //otherwise, the chord was repeated } fun void FirstChord(int note, int inversion) { if(inversion == 0) { note + 12 => tenorLine[0]; tenorLine[0] + 7 => altoLine[0]; if(majorc == 1) altoLine[0] + 9 => sopranoLine[0]; else altoLine[0] + 8 => sopranoLine[0]; } else if(inversion == 1) { } else if(inversion == 2) { } } fun void PrintAllParts() { <<<"\nsoprano:">>>; for(0 => int i; i < numNotes; i++) { PrintNote(sopranoLine[i]); } <<<"\nalto:">>>; for(0 => int i; i < numNotes; i++) { PrintNote(altoLine[i]); } <<<"\ntenor:">>>; for(0 => int i; i < numNotes; i++) { PrintNote(tenorLine[i]); } <<<"\nbass:">>>; for(0 => int i; i < numNotes; i++) { PrintNote(bassLine[i]); } <<<"done\n********************">>>; } //Iterate over bassline fun void VisitBass() { FirstChord(bassLine[0], inversions[0]); for(1 => int i; i < numNotes; i++) FillChord(i); //PrintAllParts(); } //Play notes given by the SATB lines fun void PlayNotes() { Gain g; SinOsc sop => g; SinOsc alto => g; SinOsc tenor => g; SinOsc bass => g; g => dac; g.gain(0.15); for(0 => int i; i < numNotes; i++) { if(sopranoLine[i] == 0) { sop.gain(0); } else { sop.gain(0.5); } if(altoLine[i] == 0) { alto.gain(0); } else { alto.gain(0.5); } if(tenorLine[i] == 0) { tenor.gain(0); } else { tenor.gain(0.5); } sop.freq(Std.mtof(sopranoLine[i])); alto.freq(Std.mtof(altoLine[i])); tenor.freq(Std.mtof(tenorLine[i])); bass.freq(Std.mtof(bassLine[i])); update * noteValues[i] => now; } g.gain(0); } VisitBass(); PrintAllParts(); while( true ) { tg.guess() => int i; //get the global beat i%32=> int beat; tg.mmod(i) => int m; //measure indicator //FLUTES if(beat%notelength[3]==0){ 0.2 => float gain1; module1s.freq(Std.mtof(sopranoLine[m])); module1s.noteOn(gain1); module1a.freq(Std.mtof(altoLine[m])); module1a.noteOn(gain1); module1t.freq(Std.mtof(tenorLine[m])); module1t.noteOn(gain1); } //------------------------------------------------------------------------ //SAXES if(beat%notelength[3]==0){ 0.2 => float gain2; module2s.freq(Std.mtof(sopranoLine[m])); module2s.noteOn(gain2); module2a.freq(Std.mtof(altoLine[m])); module2a.noteOn(gain2); module2t.freq(Std.mtof(tenorLine[m])); module2t.noteOn(gain2); } //------------------------------------------------------------------------ //BLOWBOTL if(beat%notelength[3]==0){ 0.4 => float gain3; module3s.freq(Std.mtof(sopranoLine[m])); module3s.noteOn(gain3); module3a.freq(Std.mtof(altoLine[m])); module3a.noteOn(gain3); module3t.freq(Std.mtof(tenorLine[m])); module3t.noteOn(gain3); } //------------------------------------------------------------------------ //GUITAR 0.6 => float gain4; arp(beat%8, 0, arpeglist[3]) => int a4; // scale(bass[m]+(i%4)*2, major ) => int note; //even notes of the scale scale(a4+bass[m], major ) => int note4; if(beat%notelength[0] ==0){ Std.mtof( 5*12 + note4 ) => module4.freq; //set note ARPEGGIO module4.noteOn( gain4 ); //play a note } //------------------------------------------------------------------------ //CLARINET 0.3 => float gain5; arp(beat%8, 0, scalerlist[0]) => int a5; scale(a5+bass[m], major) => int note5; if(beat%notelength[0] == 0){ Std.mtof( 4*12 + note5+0 ) => module5.freq; //set note ARPEGGIO module5.noteOn( gain5 ); //play a note } //------------------------------------------------------------------------ //String 4.20 => float gain6; arp(beat%8, 0, scalerlist[4]) => int a6; scale(a6+bass[m], major) => int note6; if(beat%notelength[0] == 0){ Std.mtof( 4*12 + note6+0 ) => module6.freq; //set note ARPEGGIO module6.bowPressure(1.0); module6.bowPosition(0.5); module6.volume(1.0); module6.noteOn( gain6 ); //play a note } //------------------------------------------------------------------------ //BASS GUITAR (ARP 0 2 4 6) 0.3 => float gain7; arp( beat%8, 0, arpeglist[18] ) => int a7; //the arp/melodic contour scale(a7+bass[m], major) => int note2; if(beat%notelength[0] ==0){ Std.mtof( 2*12 + note2 ) => module7.freq; //7 semitones from C is G module7.pickupPosition(1.0); module7.pluck(1.0); module7.stretch(1.0); module7.sustain(1.0); module7.noteOn( gain7 ); //play a note } //---------------------------------------------------------------------- //------------------------------------------------------------------------ //------------------------------------------------------------------------ //------------------------------------------------------------------------ //------------------------------------------------------------------------ //------------------------------------------------------------------------ tg.beat => now; //advance time one beat }